package cn.lzm.basic.service.impl;

import cn.lzm.basic.config.BussinessException;
import cn.lzm.basic.constant.BaseConstant;
import cn.lzm.basic.dto.LoginAccountDto;
import cn.lzm.basic.dto.WeChatCodeDto;
import cn.lzm.basic.service.ILoginService;
import cn.lzm.basic.util.AjaxResult;
import cn.lzm.basic.util.HttpUtil;
import cn.lzm.basic.util.MD5Utils;
import cn.lzm.basic.util.StrUtils;
import cn.lzm.user.domain.Logininfo;
import cn.lzm.user.domain.User;
import cn.lzm.user.domain.Wxuser;
import cn.lzm.user.mapper.LogininfoMapper;
import cn.lzm.user.mapper.UserMapper;
import cn.lzm.user.mapper.WxuserMapper;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Service
@Slf4j
public class LoginServiceImpl implements ILoginService {
    @Autowired
    private LogininfoMapper logininfoMapper;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private WxuserMapper wxuserMapper;
    @Autowired
    private UserMapper userMapper;

    /**
     * 账号登录
     * @param dto
     * @return
     */
    @Override
    public Map<String, Object> loginByAccount(LoginAccountDto dto) {
        // 判断参数是否是空
        // 判断账号是否存在


        Logininfo logininfo = logininfoMapper.findBYName(dto.getAccount(), dto.getType());
        if (null == logininfo) {
            throw new BussinessException("账号不存在请注册后登录！");
        }
        // 判断密码是否正确
        String encrypPassword = MD5Utils.encrypByMd5(logininfo.getSalt() + dto.getPassword());
        if (StringUtils.isBlank(encrypPassword) && !encrypPassword.equals(logininfo.getPassword())) {
            throw new BussinessException("账号和密码错误！");
        }
        // 将uuid 和 logininfo添加到redis中
        String token = UUID.randomUUID().toString();
        redisTemplate.opsForValue().set(token, logininfo, 30, TimeUnit.MINUTES);
        // 将logininfo敏感词置空
        logininfo.setPassword("");
        logininfo.setSalt("");
        logininfo.setPhone("");

        return new HashMap<String, Object>() {
            {
                put("token", token);
                put("logininfo", logininfo);
            }
        };
    }

    /**
     * 手机验证码登录
     * @param dto
     * @return
     */
    @Override
    public Map<String, Object> loginByPhone(LoginAccountDto dto) {
        if (dto.getAccount() == null) {
            throw new BussinessException("手机不能为空");
        }

        if (dto.getType() == null) {
            throw new BussinessException("手机号不正确");
        }

        String regex = "^((13[0-9])|(14[05679])|(15([0-3,5-9]))|(16[2567])|(17[01235678])|(18[0-9]|19[135689]))\\d{8}$";
        Pattern p = Pattern.compile(regex);
        Matcher m = p.matcher(dto.getAccount());
        boolean isMatch = m.matches();
        if (!isMatch) {
            throw new BussinessException("手机格式不正确");
        }


        String redisSmsCode = (String) redisTemplate.opsForValue().get(String.format(BaseConstant.VerfityCodeCons.Login_SMSCODE_PHONE, dto.getAccount()));

        if (StringUtils.isBlank(redisSmsCode)) {
            throw new BussinessException("验证码错误！");
        }
        String[] smsCode = redisSmsCode.split(":");
        if (!smsCode[0].equals(dto.getPassword())) {
            throw new BussinessException("验证码错误！");
        }
        Logininfo logininfo = logininfoMapper.findByPhone(dto.getAccount(), dto.getType());
        if (null == logininfo) {
            throw new BussinessException("该手机号没有注册！");
        }
        // 将uuid 和 logininfo添加到redis中
        String token = UUID.randomUUID().toString();
        redisTemplate.opsForValue().set(token, logininfo, 30, TimeUnit.MINUTES);
        // 将logininfo敏感词置空
        logininfo.setPassword("");
        logininfo.setSalt("");
        logininfo.setPhone("");

        return new HashMap<String, Object>() {
            {
                put("token", token);
                put("logininfo", logininfo);
            }
        };
    }

    /**
     * 微信登录
     * @param dto
     * @return
     */
    @Override
    public AjaxResult loginByWechat(WeChatCodeDto dto) {
        // 验证参数
        if (StringUtils.isBlank(dto.getCode())) {
            throw new BussinessException("参数不能为空！");
        }
        Pair<String, String> pair = this.getOpenidAndToken(dto.getCode());
        // 通过openid查询wxuser中 来判断用户之前是否使用微信登录
        Wxuser wxuser = wxuserMapper.findByOpenId(pair.getRight());
//        log.info(wxuser.toString());
        if (null != wxuser && wxuser.getUserId() != null) {
            // 通过user表关联查询logininfo表  条线 logininfo_if = id
            Logininfo logininfo = logininfoMapper.findByUserId(wxuser.getUserId());
            Map<String, Object> hashMap = getMap(logininfo);
            return AjaxResult.me().success(true).setResultObj(hashMap);
        }
        // 将token和openid返给前端 可已使用字符串拼接
        StringBuffer stringBuffer = new StringBuffer()
                .append("?accessToken=")
                .append(pair.getLeft())
                .append("&openId=")
                .append(pair.getRight());
        log.info(stringBuffer.toString());
        return AjaxResult.me().fail().setResultObj(stringBuffer);
    }

    /**
     * 将得到的数据进行封装
     * @param logininfo
     * @return
     */
    private Map<String, Object> getMap(Logininfo logininfo) {
        String token = UUID.randomUUID().toString();
        redisTemplate.opsForValue().set(token, logininfo, 30, TimeUnit.MINUTES);
        // 将logininfo敏感字段置空
        logininfo.setPassword("");
        logininfo.setSalt("");
        logininfo.setPhone("");
        Map<String, Object> hashMap = new HashMap<String, Object>() {{
            put("token", token);
            put("logininfo", logininfo);
        }};
        return hashMap;
    }

    /**
     * 手机绑定
     * @param dto
     * @return
     */
    @Override
    public Map<String, Object> wechatBinder(WeChatCodeDto dto) {
        // 校验参数是否为空
        // 验证手机格式
        // 验证手机和手机验证是否正确
        this.verifySmsCode(dto);
        // 调用微信接口获取用户信息
        String url = String.format(BaseConstant.WxLoginCons.WX_LOGIN_USER_INFO, dto.getAccessToken(),
                dto.getOpenId());
        String str = HttpUtil.httpGet(url);
        // json字符串转成 我们的实体
        Wxuser wxuser = JSONObject.parseObject(str, Wxuser.class);
        User user = userMapper.findByPhone(dto.getPhone());
        Logininfo logininfo = new Logininfo();

        if (null == user) {
            // 初始化user对象
            user = initUser(dto);
            user.setUsername(wxuser.getNickname());
            // 初始化logininfo对象
            logininfo = initLogininfo(user);
            logininfo.setUsername(wxuser.getNickname());
            // 保存 logininfo
            logininfoMapper.add(logininfo);
            // 保存user
            user.setLogininfoId(logininfo.getId());
            userMapper.add(user);

        } else {
            // 获取logininfo对象
            log.info(user.getId().toString());
            logininfo = logininfoMapper.findByUserId(user.getId());

        }
        wxuser.setUserId(user.getId());
        wxuserMapper.add(wxuser);
        // 保存wxuser
        return getMap(logininfo);

    }

    /**
     * 验证码校验
     * @param dto
     */
    private void verifySmsCode(WeChatCodeDto dto) {
        String redisSmsCode = (String) redisTemplate.opsForValue().get(
                String.format(BaseConstant.VerfityCodeCons.BINDER_SMSCODE_PHONE, dto.getPhone()));
        if (StringUtils.isBlank(redisSmsCode)) {
            throw new BussinessException("验证码错误！");
        }
        String smsCode = redisSmsCode.split(":")[0];
        if (!smsCode.equals(dto.getVerifyCode())) {
            throw new BussinessException("验证码错误！");
        }
    }

    /**
     * 初始化logininfo
     * @param user
     * @return
     */
    private Logininfo initLogininfo(User user) {
        Logininfo logininfo = new Logininfo();
        BeanUtils.copyProperties(user, logininfo);
        logininfo.setType(1);
        return logininfo;
    }

    /**
     * 初始化user
     * @param dto
     * @return
     */
    private User initUser(WeChatCodeDto dto) {
        // 盐值
        String salt = StrUtils.getComplexRandomString(32);
        // 密码
        String password = salt + "1";
        String encryPwd = MD5Utils.encrypByMd5(password);
        return User
                .builder()
                .username(dto.getPhone())
                .phone(dto.getPhone())
                .salt(salt)
                .password(encryPwd)
                .state(1)
                .createtime(new Date())
                .build();
    }

    private Pair<String,String> getOpenidAndToken(String code){
        // 使用code获取token和openid
        // 对接口地址进行拼接
        String tokenUrl = String.format(BaseConstant.WxLoginCons.WX_LOGIN_TOKEN_URL, BaseConstant.WxLoginCons.WX_LOGIN_APPID, BaseConstant.WxLoginCons.WX_LOGIN_SECRET, code);
        // 使用api访问微信接口 得到token和openid的JSON字符串
        String str = HttpUtil.httpGet(tokenUrl);
        // 将json转换成json对象
        JSONObject jsonObject = JSON.parseObject(str);
        String accessToken = jsonObject.getString("access_token");
        String openid = jsonObject.getString("openid");
        return Pair.of(accessToken,openid);
    }

}
